"""
@作者: sipeiliu@163.com
@功能: 客户端，请求更新代码
"""
import socket
import zipfile
import json
import os
import subprocess


'''客户端类'''
class ClientSocket():

    '''初始化电脑端口'''
    def __init__(self):    
        # 创建udp树莓派端套接字
        self.client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        # 绑定IP地址和端口号，空字符串默认为本机IP
        self.client_socket.bind(('', 8888))
        # 文件包存放地址
        self.zip_path = "/opt/fs_ws.zip"
        # 解压地址
        self.file_path = "/opt"

    '''接受更新'''
    def accept_update(self):
        # 设置为无限超时，等待电脑端上线
        self.client_socket.settimeout(None)
        # 接收文件大小与日期
        header_data, addr = self.client_socket.recvfrom(1024)

        header_decode = json.loads(header_data)
        size = header_decode['size']
        date = header_decode['date']
        # 创建存放数据的列表
        data_list = []
        # 设置接收超时时间
        self.client_socket.settimeout(3) 
        # 循环接收文件包并保存
        print("开始接收文件包...")
        while True:
            # 接收超时则认为已接收完毕
            try:
                file_data, addr = self.client_socket.recvfrom(1024*32)
                # 由于写速度太慢，为避免丢包，先保存在列表中
                data_list.append(file_data)
            except socket.timeout:    
                break
        # 打开写文件
        with open(self.zip_path, 'wb') as f:
            for item in data_list:
                f.write(item) 
        print("文件包接收完成...")
        # 检测文件包大小
        if size == os.stat(self.zip_path).st_size:
            # 相等则数据完整
            print("文件包大小正确...")
            # 关闭ros节点
            self.kill_ros()
            # 解压文件包
            print("开始解压文件包...")
            z = zipfile.ZipFile(self.zip_path)
            z.extractall(self.file_path)
            print("解压文件包完成...")
            # 删除文件包
            os.remove(self.zip_path)
            # 结束
            print("文件包大小:", size, "更新时间:", date)
            print("\n")
            # 启动ros节点
            self.boot_ros()
        else:
            # 发生此问题原因：网络问题导致连接断开？
            print("文件包大小错误...重试中...\n")
            # 删除错误文件包
            os.remove(self.zip_path)
            self.accept_update()
    
    
    '''关闭ros节点'''
    def kill_ros(self):
        print("关闭ros节点...")
        # 杀死与项目相关的进程
        os.system("pkill -f fs_ws")

    '''启动ros节点'''
    def boot_ros(self):
        process = subprocess.Popen( args=" chmod 777 -R /opt/fs_ws\
                                        && cd /opt/fs_ws\
                                        && source /opt/ros/foxy/setup.bash\
                                        && colcon build --symlink-install\
                                        && source /opt/fs_ws/install/setup.bash\
                                        && ros2 launch fs_launch fs_launch.py",
                                    stdout=subprocess.PIPE,
                                    stderr=subprocess.PIPE,
                                    shell=True,
                                    executable='/bin/bash') 
        print("启动ros节点...")

def main(args=None):
    # 轮询
    while True:
        # 实例化客户端对象
        client = ClientSocket()
        try:
            # 接受更新
            client.accept_update()
            # 删除对象，释放套接字
            del client
        except:
            # 未知错误，重试
            print("未知错误...重试中...\n")
            # 删除对象，释放套接字
            del client


if __name__ == '__main__':
    main()